home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 2: Applications / Linux Cubed Series 2 - Applications.iso / math / gle-3.000 / gle-3 / gle / parseafm.c < prev    next >
C/C++ Source or Header  |  1995-02-07  |  41KB  |  1,194 lines

  1.  
  2. #include "int32.h"
  3. /* parseAFM.c
  4.  *
  5.  * Copyright (c) 1988 Adobe Systems Incorporated.
  6.  * All Rights Reserved.
  7.  *
  8.  * This file is used in conjuction with the parseAFM.h header file.
  9.  * This file contains several procedures that are used to parse AFM
  10.  * files. It is intended to work with an application program that needs
  11.  * font metric information. The program can be used as is by making a
  12.  * procedure call to "parseFile" (passing in the expected parameters)
  13.  * and having it fill in a data structure with the data from the
  14.  * AFM file, or an application developer may wish to customize this
  15.  * code.
  16.  *
  17.  * There is also a file, parseAFMclient.c, that is a sample application
  18.  * showing how to call the "parseFile" procedure and how to use the data
  19.  * after "parseFile" has returned.
  20.  *
  21.  * Please read the comments in parseAFM.h and parseAFMclient.c.
  22.  *
  23.  * History:
  24.  *    original: DSM  Thu Oct 20 17:39:59 PDT 1988
  25.  */
  26. #include <stdlib.h>
  27. #include <stdio.h>
  28. #include <ctype.h>
  29. #include <string.h>
  30. #include <errno.h>
  31. #ifdef unix
  32. #include <sys/file.h>
  33. #endif
  34. #include <math.h>
  35. #include "parseafm.h"
  36. static char *token(FILE  *stream);
  37. static char *linetoken(FILE  *stream);
  38. /* static enum parseKey recognize(char *ident); */
  39. static BOOL parseGlobals(FILE *fp, GlobalFontInfo *gfi);
  40. static int initializeArray(FILE *fp, int *cwi);
  41. static parseCharWidths(FILE *fp, int *cwi);
  42. static parseCharMetrics(FILE *fp, FontInfo *fi);
  43. static parseTrackKernData(FILE *fp, FontInfo *fi);
  44. static parsePairKernData(FILE *fp, FontInfo *fi);
  45. static parseCompCharData(FILE *fp, FontInfo *fi);
  46. extern int parseFile (FILE *fp, FontInfo **fi, FLAGS flags);
  47.  
  48. #define lineterm EOL    /* line terminating character */
  49. #define normalEOF 1    /* return code from parsing routines used only */
  50.             /* in this module */
  51. #define Space "space"   /* used in string comparison to look for the width */
  52.             /* of the space character to initi the widths array */
  53. #define False "false"   /* used in string comparison to check the value of */
  54.             /* boolean keys (e.g. IsFixedPitch)  */
  55.  
  56. #define MATCH(A,B)    (strncmp((A),(B), MAX_NAME) == 0)
  57.  
  58.  
  59.  
  60.  
  61. /*************************** GLOBALS ***********************/
  62.  
  63. static char *ident = NULL; /* storage buffer for keywords */
  64.  
  65.  
  66. /* "shorts" for fast case statement
  67.  * The values of each of these enumerated items correspond to an entry in the
  68.  * table of strings defined below. Therefore, if you add a new string as
  69.  * new keyword into the keyStrings table, you must also add a corresponding
  70.  * parseKey AND it MUST be in the same position!
  71.  *
  72.  * IMPORTANT: since the sorting algorithm is a binary search, the strings of
  73.  * keywords must be placed in lexicographical order, below. [Therefore, the
  74.  * enumerated items are not necessarily in lexicographical order, depending
  75.  * on the name chosen. BUT, they must be placed in the same position as the
  76.  * corresponding key string.] The NOPE shall remain in the last position,
  77.  * since it does not correspond to any key string, and it is used in the
  78.  * "recognize" procedure to calculate how many possible keys there are.
  79.  */
  80.  
  81. static enum parseKey {
  82.   ASCENDER, CHARBBOX, CODE, COMPCHAR, CAPHEIGHT, COMMENT,
  83.   DESCENDER, ENCODINGSCHEME, ENDCHARMETRICS, ENDCOMPOSITES,
  84.   ENDFONTMETRICS, ENDKERNDATA, ENDKERNPAIRS, ENDTRACKKERN,
  85.   FAMILYNAME, FONTBBOX, FONTNAME, FULLNAME, ISFIXEDPITCH,
  86.   ITALICANGLE, KERNPAIR, KERNPAIRXAMT, LIGATURE, CHARNAME,
  87.   NOTICE, COMPCHARPIECE, STARTCHARMETRICS, STARTCOMPOSITES,
  88.   STARTFONTMETRICS, STARTKERNDATA, STARTKERNPAIRS,
  89.   STARTTRACKKERN, TRACKKERN, UNDERLINEPOSITION,
  90.   UNDERLINETHICKNESS, VERSION, XYWIDTH, XWIDTH, WEIGHT, XHEIGHT,
  91.   NOPE };
  92.  
  93. /* keywords for the system:
  94.  * This a table of all of the current strings that are vaild AFM keys.
  95.  * Each entry can be referenced by the appropriate parseKey value (an
  96.  * enumerated data type defined above). If you add a new keyword here,
  97.  * a corresponding parseKey MUST be added to the enumerated data type
  98.  * defined above, AND it MUST be added in the same position as the
  99.  * string is in this table.
  100.  *
  101.  * IMPORTANT: since the sorting algorithm is a binary search, the keywords
  102.  * must be placed in lexicographical order. And, NULL should remain at the
  103.  * end.
  104.  */
  105.  
  106. static char *keyStrings[] = {
  107.   "Ascender", "B", "C", "CC", "CapHeight", "Comment",
  108.   "Descender", "EncodingScheme", "EndCharMetrics", "EndComposites",
  109.   "EndFontMetrics", "EndKernData", "EndKernPairs", "EndTrackKern",
  110.   "FamilyName", "FontBBox", "FontName", "FullName", "IsFixedPitch",
  111.   "ItalicAngle", "KP", "KPX", "L", "N",
  112.   "Notice", "PCC", "StartCharMetrics", "StartComposites",
  113.   "StartFontMetrics", "StartKernData", "StartKernPairs",
  114.   "StartTrackKern", "TrackKern", "UnderlinePosition",
  115.   "UnderlineThickness", "Version", "W", "WX", "Weight", "XHeight",
  116.   NULL };
  117.  
  118. /*************************** PARSING ROUTINES **************/
  119.  
  120. /*************************** token *************************/
  121.  
  122. /*  A "AFM File Conventions" tokenizer. That means that it will
  123.  *  return the next token delimited by white space.  See also
  124.  *  the `linetoken' routine, which does a similar thing but
  125.  *  reads all tokens until the next end-of-line.
  126.  */
  127.  
  128. static char *token(FILE *stream)
  129. {
  130.     int ch, idx;
  131.  
  132.     /* skip over white space */
  133.     while ((ch = fgetc(stream)) == ' ' || ch == lineterm ||
  134.             ch == ',' || ch == '\t' || ch == ';');
  135.  
  136.     idx = 0;
  137.     while (ch != EOF && ch != ' ' && ch != lineterm
  138.            && ch != '\t' && ch != ':' && ch != ';')
  139.     {
  140.         ident[idx++] = ch;
  141.         ch = fgetc(stream);
  142.     } /* while */
  143.  
  144.     if (ch == EOF && idx < 1) return ((char *)NULL);
  145.     if (idx >= 1 && ch != ':' ) ungetc(ch, stream);
  146.     if (idx < 1 ) ident[idx++] = ch;    /* single-character token */
  147.     ident[idx] = 0;
  148.  
  149.     return(ident);    /* returns pointer to the token */
  150.  
  151. } /* token */
  152.  
  153.  
  154. /*************************** linetoken *************************/
  155.  
  156. /*  "linetoken" will get read all tokens until the EOL character from
  157.  *  the given stream.  This is used to get any arguments that can be
  158.  *  more than one word (like Comment lines and FullName).
  159.  */
  160.  
  161. static char *linetoken(FILE *stream)
  162. {
  163.     int ch, idx;
  164.  
  165.     while ((ch = fgetc(stream)) == ' ' || ch == '\t' );
  166.  
  167.     idx = 0;
  168.     while (ch != EOF && ch != lineterm)
  169.     {
  170.         ident[idx++] = ch;
  171.         ch = fgetc(stream);
  172.     } /* while */
  173.  
  174.     ungetc(ch, stream);
  175.     ident[idx] = 0;
  176.  
  177.     return(ident);    /* returns pointer to the token */
  178.  
  179. } /* linetoken */
  180.  
  181.  
  182. /*************************** recognize *************************/
  183.  
  184. /*  This function tries to match a string to a known list of
  185.  *  valid AFM entries (check the keyStrings array above).
  186.  *  "ident" contains everything from white space through the
  187.  *  next space, tab, or ":" character.
  188.  *
  189.  *  The algorithm is a standard Knuth binary search.
  190.  */
  191.  
  192. static enum parseKey recognize(char *ident)
  193. {
  194.     int lower = 0, upper = (int) NOPE, midpoint, cmpvalue;
  195.     BOOL found = FALSE;
  196.  
  197.     while ((upper >= lower) && !found)
  198.     {
  199.         midpoint = (lower + upper)/2;
  200.         if (keyStrings[midpoint] == NULL) break;
  201.         cmpvalue = strncmp(ident, keyStrings[midpoint], MAX_NAME);
  202.         if (cmpvalue == 0) found = TRUE;
  203.         else if (cmpvalue < 0) upper = midpoint - 1;
  204.         else lower = midpoint + 1;
  205.     } /* while */
  206.  
  207.     if (found) return (enum parseKey) midpoint;
  208.     else return NOPE;
  209.  
  210. } /* recognize */
  211.  
  212.  
  213. /************************* parseGlobals *****************************/
  214.  
  215. /*  This function is called by "parseFile". It will parse the AFM File
  216.  *  up to the "StartCharMetrics" keyword, which essentially marks the
  217.  *  end of the Global Font Information and the beginning of the character
  218.  *  metrics information.
  219.  *
  220.  *  If the caller of "parseFile" specified that it wanted the Global
  221.  *  Font Information (as defined by the "AFM File Specification"
  222.  *  document), then that information will be stored in the returned
  223.  *  data structure.
  224.  *
  225.  *  Any Global Font Information entries that are not found in a
  226.  *  given file, will have the usual default initialization value
  227.  *  for its type (i.e. entries of type int will be 0, etc).
  228.  *
  229.  *  This function returns an error code specifying whether there was
  230.  *  a premature EOF or a parsing error. This return value is used by
  231.  *  parseFile to determine if there is more file to parse.
  232.  */
  233.  
  234. static BOOL parseGlobals(FILE *fp, GlobalFontInfo *gfi)
  235. {
  236.     BOOL cont = TRUE, save = (gfi != NULL);
  237.     int error = ok;
  238.     register char *keyword;
  239.  
  240.     while (cont)
  241.     {
  242.         keyword = token(fp);
  243.  
  244.         if (keyword == NULL)
  245.           /* Have reached an early and unexpected EOF. */
  246.           /* Set flag and stop parsing */
  247.         {
  248.             error = earlyEOF;
  249.             break;   /* get out of loop */
  250.         }
  251.         if (!save)
  252.           /* get tokens until the end of the Global Font info section */
  253.           /* without saving any of the data */
  254.             switch (recognize(keyword))
  255.             {
  256.                 case STARTCHARMETRICS:
  257.                     cont = FALSE;
  258.                     break;
  259.                 case ENDFONTMETRICS:
  260.                     cont = FALSE;
  261.                     error = normalEOF;
  262.                     break;
  263.                 default:
  264.                     break;
  265.             } /* switch */
  266.         else
  267.           /* otherwise parse entire global font info section, */
  268.           /* saving the data */
  269.             switch(recognize(keyword))
  270.             {
  271.                 case STARTFONTMETRICS:
  272.                     keyword = token(fp);
  273.             gfi->afmVersion = (char *) calloc(1, 1+strlen(keyword));
  274.                     strcpy(gfi->afmVersion, keyword);
  275.                     break;
  276.                 case COMMENT:
  277.                     keyword = linetoken(fp);
  278.                     break;
  279.                 case FONTNAME:
  280.                     keyword = token(fp);
  281.             gfi->fontName = (char *) calloc(1, 1+strlen(keyword));
  282.                     strcpy(gfi->fontName, keyword);
  283.                     break;
  284.                 case ENCODINGSCHEME:
  285.                     keyword = token(fp);
  286.             gfi->encodingScheme = (char *) calloc(1, 1+strlen(keyword));
  287.                     strcpy(gfi->encodingScheme, keyword);
  288.                     break;
  289.                 case FULLNAME:
  290.                     keyword = linetoken(fp);
  291.             gfi->fullName = (char *) calloc(1, 1+strlen(keyword));
  292.                     strcpy(gfi->fullName, keyword);
  293.                     break;
  294.                 case FAMILYNAME:
  295.                     keyword = linetoken(fp);
  296.             gfi->familyName = (char *) calloc(1, 1+strlen(keyword));
  297.                     strcpy(gfi->familyName, keyword);
  298.                     break;
  299.                 case WEIGHT:
  300.                     keyword = token(fp);
  301.             gfi->weight = (char *) calloc(1, 1+strlen(keyword));
  302.                     strcpy(gfi->weight, keyword);
  303.                     break;
  304.                 case ITALICANGLE:
  305.                     keyword = token(fp);
  306.                     gfi->italicAngle = atof(keyword);
  307.                     if (errno == ERANGE) {printf("Range error \n"); error = parseError;}
  308.                     break;
  309.                 case ISFIXEDPITCH:
  310.                     keyword = token(fp);
  311.                     if (MATCH(keyword, False))
  312.                         gfi->isFixedPitch = 0;
  313.                     else
  314.                         gfi->isFixedPitch = 1;
  315.                     break;
  316.             case UNDERLINEPOSITION:
  317.                     keyword = token(fp);
  318.                 gfi->underlinePosition = atoi(keyword);
  319.                     break;
  320.                 case UNDERLINETHICKNESS:
  321.                     keyword = token(fp);
  322.                     gfi->underlineThickness = atoi(keyword);
  323.                     break;
  324.                 case VERSION:
  325.                     keyword = token(fp);
  326.             gfi->version = (char *) calloc(1, 1+strlen(keyword));
  327.                     strcpy(gfi->version, keyword);
  328.                     break;
  329.                 case NOTICE:
  330.                     keyword = linetoken(fp);
  331.             gfi->notice = (char *) calloc(1, 1+strlen(keyword));
  332.                     strcpy(gfi->notice, keyword);
  333.                     break;
  334.                 case FONTBBOX:
  335.                     keyword = token(fp);
  336.                     gfi->fontBBox.llx = atoi(keyword);
  337.                     keyword = token(fp);
  338.                     gfi->fontBBox.lly = atoi(keyword);
  339.                     keyword = token(fp);
  340.                     gfi->fontBBox.urx = atoi(keyword);
  341.                     keyword = token(fp);
  342.                     gfi->fontBBox.ury = atoi(keyword);
  343.                     break;
  344.                 case CAPHEIGHT:
  345.                     keyword = token(fp);
  346.                     gfi->capHeight = atoi(keyword);
  347.                     break;
  348.                 case XHEIGHT:
  349.                     keyword = token(fp);
  350.                     gfi->xHeight = atoi(keyword);
  351.                     break;
  352.                 case DESCENDER:
  353.                     keyword = token(fp);
  354.                     gfi->descender = atoi(keyword);
  355.                     break;
  356.                 case ASCENDER:
  357.                     keyword = token(fp);
  358.                     gfi->ascender = atoi(keyword);
  359.                     break;
  360.                 case STARTCHARMETRICS:
  361.                     cont = FALSE;
  362.                     break;
  363.                 case ENDFONTMETRICS:
  364.                     cont = FALSE;
  365.                     error = normalEOF;
  366.                     break;
  367.                 case NOPE:
  368.                 default:
  369.             printf("Unrecognized key word {%s} \n",keyword);
  370.                     error = parseError;
  371.                     break;
  372.             } /* switch */
  373.     } /* while */
  374.  
  375.     return(error);
  376.  
  377. } /* parseGlobals */
  378.  
  379.  
  380.  
  381. /************************* initializeArray ************************/
  382.  
  383. /*  Unmapped character codes are (at Adobe Systems) assigned the
  384.  *  width of the space character (if one exists) else they get the
  385.  *  value of 250 ems. This function initializes all entries in the
  386.  *  char widths array to have this value. Then any mapped character
  387.  *  codes will be replaced with the width of the appropriate character
  388.  *  when parsing the character metric section.
  389.  
  390.  *  This function parses the Character Metrics Section looking
  391.  *  for a space character (by comparing character names). If found,
  392.  *  the width of the space character will be used to initialize the
  393.  *  values in the array of character widths.
  394.  *
  395.  *  Before returning, the position of the read/write pointer of the
  396.  *  file is reset to be where it was upon entering this function.
  397.  */
  398.  
  399. static int initializeArray(FILE *fp, int *cwi)
  400. {
  401.     BOOL cont = TRUE, found = FALSE;
  402.     int32 opos = ftell(fp);
  403.     int code = 0, width = 0, i = 0, error = 0;
  404.     register char *keyword;
  405.  
  406.     while (cont)
  407.     {
  408.         keyword = token(fp);
  409.         if (keyword == NULL)
  410.         {
  411.             error = earlyEOF;
  412.             break; /* get out of loop */
  413.         }
  414.         switch(recognize(keyword))
  415.         {
  416.             case COMMENT:
  417.                 keyword = linetoken(fp);
  418.                 break;
  419.             case CODE:
  420.                 code = atoi(token(fp));
  421.                 break;
  422.             case XWIDTH:
  423.                 width = atoi(token(fp));
  424.                 break;
  425.             case CHARNAME:
  426.                 keyword = token(fp);
  427.                 if (MATCH(keyword, Space))
  428.                 {
  429.                     cont = FALSE;
  430.                     found = TRUE;
  431.                 }
  432.                 break;
  433.             case ENDCHARMETRICS:
  434.                 cont = FALSE;
  435.                 break;
  436.             case ENDFONTMETRICS:
  437.                 cont = FALSE;
  438.                 error = normalEOF;
  439.                 break;
  440.             case NOPE:
  441.             default:
  442.         printf("Unrecognized key (level 1) word {%s} \n",keyword);
  443.                 error = parseError;
  444.                 break;
  445.         } /* switch */
  446.     } /* while */
  447.  
  448.     if (!found)
  449.     width = 250;
  450.  
  451.     for (i = 0; i < 256; ++i)
  452.         cwi[i] = width;
  453.  
  454.     fseek(fp, opos, 0);
  455.  
  456.     return(error);
  457.  
  458. } /* initializeArray */
  459.  
  460.  
  461. /************************* parseCharWidths **************************/
  462.  
  463. /*  This function is called by "parseFile". It will parse the AFM File
  464.  *  up to the "EndCharMetrics" keyword. It will save the character
  465.  *  width info (as opposed to all of the character metric information)
  466.  *  if requested by the caller of parseFile. Otherwise, it will just
  467.  *  parse through the section without saving any information.
  468.  *
  469.  *  If data is to be saved, parseCharWidths is passed in a pointer
  470.  *  to an array of widths that has already been initialized by the
  471.  *  standard value for unmapped character codes. This function parses
  472.  *  the Character Metrics section only storing the width information
  473.  *  for the encoded characters into the array using the character code
  474.  *  as the index into that array.
  475.  *
  476.  *  This function returns an error code specifying whether there was
  477.  *  a premature EOF or a parsing error. This return value is used by
  478.  *  parseFile to determine if there is more file to parse.
  479.  */
  480.  
  481. static parseCharWidths(FILE *fp, int *cwi)
  482. {
  483.     BOOL cont = TRUE, save = (cwi != NULL);
  484.     int pos = 0, error = ok;
  485.     register char *keyword;
  486.  
  487.     while (cont)
  488.     {
  489.         keyword = token(fp);
  490.           /* Have reached an early and unexpected EOF. */
  491.           /* Set flag and stop parsing */
  492.         if (keyword == NULL)
  493.         {
  494.             error = earlyEOF;
  495.             break; /* get out of loop */
  496.         }
  497.         if (!save)
  498.           /* get tokens until the end of the Char Metrics section without */
  499.           /* saving any of the data*/
  500.             switch (recognize(keyword))
  501.             {
  502.                 case ENDCHARMETRICS:
  503.                     cont = FALSE;
  504.                     break;
  505.                 case ENDFONTMETRICS:
  506.                     cont = FALSE;
  507.                     error = normalEOF;
  508.                     break;
  509.                 default:
  510.                     break;
  511.             } /* switch */
  512.         else
  513.           /* otherwise parse entire char metrics section, saving */
  514.           /* only the char x-width info */
  515.             switch(recognize(keyword))
  516.             {
  517.                 case COMMENT:
  518.                     keyword = linetoken(fp);
  519.                     break;
  520.                 case CODE:
  521.                     keyword = token(fp);
  522.                     pos = atoi(keyword);
  523.                     break;
  524.                 case XYWIDTH:
  525.                 /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */
  526.                     keyword = token(fp); keyword = token(fp); /* eat values */
  527.                     error = parseError;
  528.                     break;
  529.                 case XWIDTH:
  530.                     keyword = token(fp);
  531.                     if (pos >= 0) /* ignore unmapped chars */
  532.                         cwi[pos] = atoi(keyword);
  533.                     break;
  534.                 case ENDCHARMETRICS:
  535.                     cont = FALSE;
  536.                     break;
  537.                 case ENDFONTMETRICS:
  538.                     cont = FALSE;
  539.                     error = normalEOF;
  540.                     break;
  541.                 case CHARNAME:    /* eat values (so doesn't cause parseError) */
  542.                     keyword = token(fp);
  543.                     break;
  544.                 case CHARBBOX:
  545.                     keyword = token(fp); keyword = token(fp);
  546.                     keyword = token(fp); keyword = token(fp);
  547.             break;
  548.         case LIGATURE:
  549.                     keyword = token(fp); keyword = token(fp);
  550.             break;
  551.                 case NOPE:
  552.                 default:
  553.             printf("Unrecognized key (charstuff) word {%s} \n",keyword);
  554.                     error = parseError;
  555.                     break;
  556.             } /* switch */
  557.     } /* while */
  558.  
  559.     return(error);
  560.  
  561. } /* parseCharWidths */
  562.  
  563.  
  564. /************************* parseCharMetrics ************************/
  565.  
  566. /*  This function is called by parseFile if the caller of parseFile
  567.  *  requested that all character metric information be saved
  568.  *  (as opposed to only the character width information).
  569.  *
  570.  *  parseCharMetrics is passed in a pointer to an array of records
  571.  *  to hold information on a per character basis. This function
  572.  *  parses the Character Metrics section storing all character
  573.  *  metric information for the ALL characters (mapped and unmapped)
  574.  *  into the array.
  575.  *
  576.  *  This function returns an error code specifying whether there was
  577.  *  a premature EOF or a parsing error. This return value is used by
  578.  *  parseFile to determine if there is more file to parse.
  579.  */
  580.  
  581. static parseCharMetrics(FILE *fp, FontInfo *fi)
  582. {
  583.     BOOL cont = TRUE, firstTime = TRUE;
  584.     int error = ok, count = 0;
  585.     register CharMetricInfo *temp = fi->cmi;
  586.     register char *keyword;
  587.  
  588.     while (cont)
  589.     {
  590.         keyword = token(fp);
  591.         if (keyword == NULL)
  592.         {
  593.             error = earlyEOF;
  594.             break; /* get out of loop */
  595.         }
  596.         switch(recognize(keyword))
  597.         {
  598.             case COMMENT:
  599.                 keyword = linetoken(fp);
  600.                 break;
  601.             case CODE:
  602.                 if (count < fi->numOfChars)
  603.                 {
  604.                     if (firstTime) firstTime = FALSE;
  605.                     else temp++;
  606.                     temp->code = atoi(token(fp));
  607.                     count++;
  608.                 }
  609.                 else
  610.                 {
  611.             printf("Too many numofchars \n");
  612.                     error = parseError;
  613.                     cont = FALSE;
  614.                 }
  615.                 break;
  616.             case XYWIDTH:
  617.                 temp->wx = atoi(token(fp));
  618.                 temp->wy = atoi(token(fp));
  619.                 break;
  620.             case XWIDTH:
  621.                 temp->wx = atoi(token(fp));
  622.                 break;
  623.             case CHARNAME:
  624.                 keyword = token(fp);
  625.         temp->name = (char *) calloc(1, 1+strlen(keyword));
  626.                 strcpy(temp->name, keyword);
  627.                 break;
  628.             case CHARBBOX:
  629.                 temp->charBBox.llx = atoi(token(fp));
  630.                 temp->charBBox.lly = atoi(token(fp));
  631.                 temp->charBBox.urx = atoi(token(fp));
  632.                 temp->charBBox.ury = atoi(token(fp));
  633.                 break;
  634.             case LIGATURE: {
  635.                 Ligature **tail = &(temp->ligs);
  636.                 Ligature *node = *tail;
  637.  
  638.                 if (*tail != NULL)
  639.                 {
  640.                     while (node->next != NULL)
  641.                         node = node->next;
  642.                     tail = &(node->next);
  643.                 }
  644.  
  645.                 *tail = (Ligature *) calloc(1, sizeof(Ligature));
  646.                 keyword = token(fp);
  647.         (*tail)->succ = (char *) calloc(1, strlen(keyword)+1);
  648.                 strcpy((*tail)->succ, keyword);
  649.                 keyword = token(fp);
  650.         (*tail)->lig = (char *) calloc(1, strlen(keyword)+1);
  651.         strcpy((*tail)->lig, keyword);
  652. /*        printf("lig {%s} {%s} \n",(*tail)->lig,(*tail)->succ);
  653.         printf("ligd {%d} {%d} \n",(*tail)->lig,(*tail)->succ);
  654.   */
  655.               break; }
  656.             case ENDCHARMETRICS:
  657.                 cont = FALSE;;
  658.                 break;
  659.             case ENDFONTMETRICS:
  660.                 cont = FALSE;
  661.                 error = normalEOF;
  662.                 break;
  663.             case NOPE:
  664.             default:
  665.         printf("Unrecognized key (Top level) word {%s} \n",keyword);
  666.                 error = parseError;
  667.                 break;
  668.         } /* switch */
  669.     } /* while */
  670.  
  671.     if ((error == ok) && (count != fi->numOfChars)) {
  672.     printf("wrong num of chars got %d, wanted %d \n",count,fi->numOfChars);
  673.         error = parseError;
  674.     }
  675.     return(error);
  676.  
  677. } /* parseCharMetrics */
  678.  
  679.  
  680.  
  681. /************************* parseTrackKernData ***********************/
  682.  
  683. /*  This function is called by "parseFile". It will parse the AFM File
  684.  *  up to the "EndTrackKern" or "EndKernData" keywords. It will save the
  685.  *  track kerning data if requested by the caller of parseFile.
  686.  *
  687.  *  parseTrackKernData is passed in a pointer to the FontInfo record.
  688.  *  If data is to be saved, the FontInfo record will already contain
  689.  *  a valid pointer to storage for the track kerning data.
  690.  *
  691.  *  This function returns an error code specifying whether there was
  692.  *  a premature EOF or a parsing error. This return value is used by
  693.  *  parseFile to determine if there is more file to parse.
  694.  */
  695.  
  696. static parseTrackKernData(FILE *fp, FontInfo *fi)
  697. {
  698.     BOOL cont = TRUE, save = (fi->tkd != NULL);
  699.     int pos = 0, error = ok, tcount = 0;
  700.     register char *keyword;
  701.  
  702.     while (cont)
  703.     {
  704.         keyword = token(fp);
  705.  
  706.         if (keyword == NULL)
  707.         {
  708.             error = earlyEOF;
  709.             break; /* get out of loop */
  710.         }
  711.         if (!save)
  712.           /* get tokens until the end of the Track Kerning Data */
  713.           /* section without saving any of the data */
  714.             switch(recognize(keyword))
  715.             {
  716.                 case ENDTRACKKERN:
  717.                 case ENDKERNDATA:
  718.                     cont = FALSE;
  719.                     break;
  720.                 case ENDFONTMETRICS:
  721.                     cont = FALSE;
  722.                     error = normalEOF;
  723.                     break;
  724.                 default:
  725.                     break;
  726.             } /* switch */
  727.     else
  728.           /* otherwise parse entire Track Kerning Data section, */
  729.           /* saving the data */
  730.             switch(recognize(keyword))
  731.             {
  732.                 case COMMENT:
  733.                     keyword = linetoken(fp);
  734.                     break;
  735.                 case TRACKKERN:
  736.                     if (tcount < fi->numOfTracks)
  737.                     {
  738.                         keyword = token(fp);
  739.                         fi->tkd[pos].degree = atoi(keyword);
  740.                         keyword = token(fp);
  741.                         fi->tkd[pos].minPtSize = atof(keyword);
  742.                         if (errno == ERANGE) error = parseError;
  743.                         keyword = token(fp);
  744.                         fi->tkd[pos].minKernAmt = atof(keyword);
  745.                         if (errno == ERANGE) error = parseError;
  746.                         keyword = token(fp);
  747.                         fi->tkd[pos].maxPtSize = atof(keyword);
  748.                         if (errno == ERANGE) error = parseError;
  749.                         keyword = token(fp);
  750.                         fi->tkd[pos++].maxKernAmt = atof(keyword);
  751.                         if (errno == ERANGE) error = parseError;
  752.                         tcount++;
  753.                     }
  754.                     else
  755.                     {
  756.             printf("Too many tracks \n");
  757.                         error = parseError;
  758.                         cont = FALSE;
  759.                     }
  760.                     break;
  761.                 case ENDTRACKKERN:
  762.                 case ENDKERNDATA:
  763.                     cont = FALSE;
  764.                     break;
  765.                 case ENDFONTMETRICS:
  766.                     cont = FALSE;
  767.                     error = normalEOF;
  768.                     break;
  769.                 case NOPE:
  770.                 default:
  771.         printf("Unrecognized key (zzz level) word {%s} \n",keyword);
  772.                     break;
  773.             } /* switch */
  774.     } /* while */
  775.  
  776.     if (error == ok && tcount != fi->numOfTracks) {
  777.         error = parseError;
  778.     printf("Wrong num of tracks %d \n",tcount);
  779.     }
  780.     return(error);
  781.  
  782. } /* parseTrackKernData */
  783.  
  784.  
  785. /************************* parsePairKernData ************************/
  786.  
  787. /*  This function is called by "parseFile". It will parse the AFM File
  788.  *  up to the "EndKernPairs" or "EndKernData" keywords. It will save
  789.  *  the pair kerning data if requested by the caller of parseFile.
  790.  *
  791.  *  parsePairKernData is passed in a pointer to the FontInfo record.
  792.  *  If data is to be saved, the FontInfo record will already contain
  793.  *  a valid pointer to storage for the pair kerning data.
  794.  *
  795.  *  This function returns an error code specifying whether there was
  796.  *  a premature EOF or a parsing error. This return value is used by
  797.  *  parseFile to determine if there is more file to parse.
  798.  */
  799.  
  800. static parsePairKernData(FILE *fp, FontInfo *fi)
  801. {
  802.     BOOL cont = TRUE, save = (fi->pkd != NULL);
  803.     int pos = 0, error = ok, pcount = 0;
  804.     register char *keyword;
  805.  
  806.     while (cont)
  807.     {
  808.         keyword = token(fp);
  809.  
  810.         if (keyword == NULL)
  811.         {
  812.             error = earlyEOF;
  813.             break; /* get out of loop */
  814.         }
  815.         if (!save)
  816.           /* get tokens until the end of the Pair Kerning Data */
  817.           /* section without saving any of the data */
  818.             switch(recognize(keyword))
  819.             {
  820.                 case ENDKERNPAIRS:
  821.                 case ENDKERNDATA:
  822.                     cont = FALSE;
  823.                     break;
  824.                 case ENDFONTMETRICS:
  825.                     cont = FALSE;
  826.                     error = normalEOF;
  827.                     break;
  828.                 default:
  829.                     break;
  830.             } /* switch */
  831.     else
  832.           /* otherwise parse entire Pair Kerning Data section, */
  833.           /* saving the data */
  834.             switch(recognize(keyword))
  835.             {
  836.                 case COMMENT:
  837.                     keyword = linetoken(fp);
  838.                     break;
  839.                 case KERNPAIR:
  840.                     if (pcount < fi->numOfPairs)
  841.                     {
  842.                         keyword = token(fp);
  843.                         fi->pkd[pos].name1 = (char *)
  844.                 calloc(1,1+ strlen(keyword));
  845.                         strcpy(fi->pkd[pos].name1, keyword);
  846.                         keyword = token(fp);
  847.                         fi->pkd[pos].name2 = (char *)
  848.                 calloc(1, 1+strlen(keyword));
  849.                         strcpy(fi->pkd[pos].name2, keyword);
  850.                         keyword = token(fp);
  851.                         fi->pkd[pos].xamt = atoi(keyword);
  852.                         keyword = token(fp);
  853.                         fi->pkd[pos++].yamt = atoi(keyword);
  854.                         pcount++;
  855.                     }
  856.                     else
  857.                     {
  858.             printf("Too many pairs \n");
  859.                         error = parseError;
  860.                         cont = FALSE;
  861.                     }
  862.                     break;
  863.                 case KERNPAIRXAMT:
  864.                     if (pcount < fi->numOfPairs)
  865.                     {
  866.                         keyword = token(fp);
  867.                         fi->pkd[pos].name1 = (char *)
  868.                             calloc(1, strlen(keyword));
  869.                         strcpy(fi->pkd[pos].name1, keyword);
  870.                         keyword = token(fp);
  871.                         fi->pkd[pos].name2 = (char *)
  872.                             calloc(1, 1+strlen(keyword));
  873.                         strcpy(fi->pkd[pos].name2, keyword);
  874.                         keyword = token(fp);
  875.                         fi->pkd[pos++].xamt = atoi(keyword);
  876.                         pcount++;
  877.                     }
  878.                     else
  879.                     {
  880.             printf("Too many pairs2 \n");
  881.                         error = parseError;
  882.                         cont = FALSE;
  883.                     }
  884.                     break;
  885.                 case ENDKERNPAIRS:
  886.                 case ENDKERNDATA:
  887.                     cont = FALSE;
  888.                     break;
  889.                 case ENDFONTMETRICS:
  890.                     cont = FALSE;
  891.                     error = normalEOF;
  892.                     break;
  893.                 case NOPE:
  894.                 default:
  895.         printf("Unrecognized key (Top level aas) word {%s} \n",keyword);
  896.         error = parseError;
  897.                     break;
  898.             } /* switch */
  899.     } /* while */
  900.  
  901.     if (error == ok && pcount != fi->numOfPairs) {
  902.     printf("wrong number of pcountss \n");
  903.         error = parseError;
  904.     }
  905.  
  906.     return(error);
  907.  
  908. } /* parsePairKernData */
  909.  
  910.  
  911. /************************* parseCompCharData **************************/
  912.  
  913. /*  This function is called by "parseFile". It will parse the AFM File
  914.  *  up to the "EndComposites" keyword. It will save the composite
  915.  *  character data if requested by the caller of parseFile.
  916.  *
  917.  *  parseCompCharData is passed in a pointer to the FontInfo record, and
  918.  *  a boolean representing if the data should be saved.
  919.  *
  920.  *  This function will create the appropriate amount of storage for
  921.  *  the composite character data and store a pointer to the storage
  922.  *  in the FontInfo record.
  923.  *
  924.  *  This function returns an error code specifying whether there was
  925.  *  a premature EOF or a parsing error. This return value is used by
  926.  *  parseFile to determine if there is more file to parse.
  927.  */
  928.  
  929. static parseCompCharData(FILE *fp, FontInfo *fi)
  930. {
  931.     BOOL cont = TRUE, firstTime = TRUE, save = (fi->ccd != NULL);
  932.     int pos = 0, j = 0, error = ok, ccount = 0, pcount = 0;
  933.     register char *keyword;
  934.  
  935.     while (cont)
  936.     {
  937.         keyword = token(fp);
  938.         if (keyword == NULL)
  939.           /* Have reached an early and unexpected EOF. */
  940.           /* Set flag and stop parsing */
  941.         {
  942.             error = earlyEOF;
  943.             break; /* get out of loop */
  944.         }
  945.         if (ccount > fi->numOfComps)
  946.         {
  947.             error = parseError;
  948.             break; /* get out of loop */
  949.         }
  950.         if (!save)
  951.           /* get tokens until the end of the Composite Character info */
  952.           /* section without saving any of the data */
  953.             switch(recognize(keyword))
  954.             {
  955.                 case ENDCOMPOSITES:
  956.                     cont = FALSE;
  957.                     break;
  958.                 case ENDFONTMETRICS:
  959.                     cont = FALSE;
  960.                     error = normalEOF;
  961.                     break;
  962.                 default:
  963.                     break;
  964.             } /* switch */
  965.     else
  966.           /* otherwise parse entire Composite Character info section, */
  967.           /* saving the data */
  968.             switch(recognize(keyword))
  969.             {
  970.                 case COMMENT:
  971.                     keyword = linetoken(fp);
  972.                     break;
  973.                 case COMPCHAR:
  974.                     if (ccount < fi->numOfComps)
  975.                     {
  976.                         keyword = token(fp);
  977.                         if (pcount != fi->ccd[pos].numOfPieces)
  978.                             error = parseError;
  979.                         pcount = 0;
  980.                         if (firstTime) firstTime = FALSE;
  981.                         else pos++;
  982.                         fi->ccd[pos].ccName = (char *)
  983.                             calloc(1, 1+strlen(keyword));
  984.                         strcpy(fi->ccd[pos].ccName, keyword);
  985.                         keyword = token(fp);
  986.                         fi->ccd[pos].numOfPieces = atoi(keyword);
  987.                         fi->ccd[pos].pieces = (Pcc *)
  988.                             calloc(fi->ccd[pos].numOfPieces, sizeof(Pcc));
  989.                         j = 0;
  990.                         ccount++;
  991.                     }
  992.                     else
  993.                     {
  994.                         error = parseError;
  995.                         cont = FALSE;
  996.                     }
  997.                     break;
  998.                 case COMPCHARPIECE:
  999.                     if (pcount < fi->ccd[pos].numOfPieces)
  1000.                     {
  1001.                         keyword = token(fp);
  1002.                         fi->ccd[pos].pieces[j].pccName = (char *)
  1003.                                 calloc(1, 1+strlen(keyword));
  1004.                         strcpy(fi->ccd[pos].pieces[j].pccName, keyword);
  1005.                         keyword = token(fp);
  1006.                         fi->ccd[pos].pieces[j].deltax = atoi(keyword);
  1007.                         keyword = token(fp);
  1008.                         fi->ccd[pos].pieces[j++].deltay = atoi(keyword);
  1009.                         pcount++;
  1010.                     }
  1011.                     else
  1012.                         error = parseError;
  1013.                     break;
  1014.                 case ENDCOMPOSITES:
  1015.                     cont = FALSE;
  1016.                     break;
  1017.                 case ENDFONTMETRICS:
  1018.                     cont = FALSE;
  1019.                     error = normalEOF;
  1020.                     break;
  1021.                 case NOPE:
  1022.                 default:
  1023.                     error = parseError;
  1024.                     break;
  1025.             } /* switch */
  1026.     } /* while */
  1027.  
  1028.     if (error == ok && ccount != fi->numOfComps)
  1029.         error = parseError;
  1030.  
  1031.     return(error);
  1032.  
  1033. } /* parseCompCharData */
  1034.  
  1035.  
  1036.  
  1037.  
  1038. /*************************** 'PUBLIC' FUNCTION ********************/
  1039.  
  1040.  
  1041. /*************************** parseFile *****************************/
  1042.  
  1043. /*  parseFile is the only 'public' procedure available. It is called
  1044.  *  from an application wishing to get information from an AFM file.
  1045.  *  The caller of this function is responsible for locating and opening
  1046.  *  an AFM file and handling all errors associated with that task.
  1047.  *
  1048.  *  parseFile expects 3 parameters: a vaild file pointer, a pointer
  1049.  *  to a (FontInfo *) variable (for which storage will be allocated and
  1050.  *  the data requested filled in), and a mask specifying which
  1051.  *  data from the AFM File should be saved in the FontInfo structure.
  1052.  *
  1053.  *  The file will be parsed and the requested data will be stored in
  1054.  *  a record of type FontInfo (refer to ParseAFM.h).
  1055.  *
  1056.  *  parseFile returns an error code as defined in parseAFM.h.
  1057.  *
  1058.  *  The position of the read/write pointer associated with the file
  1059.  *  pointer upon return of this function is undefined.
  1060.  */
  1061.  
  1062. extern int parseFile (FILE *fp, FontInfo **fi, FLAGS flags)
  1063. {
  1064.  
  1065.     int code = ok;     /* return code from each of the parsing routines */
  1066.     int error = ok;    /* used as the return code from this function */
  1067.  
  1068.     register char *keyword; /* used to store a token */
  1069.  
  1070.  
  1071.     /* storage data for the global variable ident */
  1072.     ident = (char *) calloc(MAX_NAME, sizeof(char));
  1073.  
  1074.     (*fi) = (FontInfo *) calloc(1, sizeof(FontInfo));
  1075.  
  1076.     if (flags & P_G)
  1077.         (*fi)->gfi = (GlobalFontInfo *) calloc(1, sizeof(GlobalFontInfo));
  1078.  
  1079.     /* The AFM File begins with Global Font Information. This section */
  1080.     /* will be parsed whether or not information should be saved. */
  1081.     code = parseGlobals(fp, (*fi)->gfi);
  1082.  
  1083.     if (code < 0) error = code;
  1084.  
  1085.     /* The Global Font Information is followed by the Character Metrics */
  1086.     /* section. Which procedure is used to parse this section depends on */
  1087.     /* how much information should be saved. If all of the metrics info */
  1088.     /* is wanted, parseCharMetrics is called. If only the character widths */
  1089.     /* is wanted, parseCharWidths is called. parseCharWidths will also */
  1090.     /* be called in the case that no character data is to be saved, just */
  1091.     /* to parse through the section. */
  1092.  
  1093.     if ((code != normalEOF) && (code != earlyEOF))
  1094.     {
  1095.         (*fi)->numOfChars = atoi(token(fp));
  1096.     if (flags & (P_M ^ P_W))
  1097.         {
  1098.             (*fi)->cmi = (CharMetricInfo *)
  1099.                       calloc((*fi)->numOfChars, sizeof(CharMetricInfo));
  1100.             code = parseCharMetrics(fp, *fi);
  1101.         }
  1102.         else
  1103.         {
  1104.             if (flags & P_W)
  1105.                 (*fi)->cwi = (int *) calloc(256, sizeof(int));
  1106.             /* parse section regardless */
  1107.             code = parseCharWidths(fp, (*fi)->cwi);
  1108.         } /* else */
  1109.     } /* if */
  1110.  
  1111.     if ((error != earlyEOF) && (code < 0)) error = code;
  1112.  
  1113.     /* The remaining sections of the AFM are optional. This code will */
  1114.     /* look at the next keyword in the file to determine what section */
  1115.     /* is next, and then allocate the appropriate amount of storage */
  1116.     /* for the data (if the data is to be saved) and call the */
  1117.     /* appropriate parsing routine to parse the section. */
  1118.  
  1119.     while ((code != normalEOF) && (code != earlyEOF))
  1120.     {
  1121.         keyword = token(fp);
  1122.         if (keyword == NULL)
  1123.           /* Have reached an early and unexpected EOF. */
  1124.           /* Set flag and stop parsing */
  1125.         {
  1126.             code = earlyEOF;
  1127.             break; /* get out of loop */
  1128.         }
  1129.         switch(recognize(keyword))
  1130.         {
  1131.             case STARTKERNDATA:
  1132.                 break;
  1133.             case ENDKERNDATA:
  1134.                 break;
  1135.             case STARTTRACKKERN:
  1136.                 keyword = token(fp);
  1137.                 if (flags & P_T)
  1138.                 {
  1139.                     (*fi)->numOfTracks = atoi(keyword);
  1140.                     (*fi)->tkd = (TrackKernData *)
  1141.                         calloc((*fi)->numOfTracks, sizeof(TrackKernData));
  1142.                 } /* if */
  1143.                 code = parseTrackKernData(fp, *fi);
  1144.                 break;
  1145.             case STARTKERNPAIRS:
  1146.                 keyword = token(fp);
  1147.                 if (flags & P_P)
  1148.                 {
  1149.                     (*fi)->numOfPairs = atoi(keyword);
  1150.                     (*fi)->pkd = (PairKernData *)
  1151.                         calloc((*fi)->numOfPairs, sizeof(PairKernData));
  1152.                 } /* if */
  1153.                 code = parsePairKernData(fp, *fi);
  1154.                 break;
  1155.             case STARTCOMPOSITES:
  1156.                 keyword = token(fp);
  1157.                 if (flags & P_C)
  1158.                 {
  1159.                     (*fi)->numOfComps = atoi(keyword);
  1160.                     (*fi)->ccd = (CompCharData *)
  1161.                         calloc((*fi)->numOfComps, sizeof(CompCharData));
  1162.                 } /* if */
  1163.                 code = parseCompCharData(fp, *fi);
  1164.                 break;
  1165.             case ENDFONTMETRICS:
  1166.                 code = normalEOF;
  1167.                 break;
  1168.             case NOPE:
  1169.             default:
  1170.                 code = parseError;
  1171.                 break;
  1172.         } /* switch */
  1173.  
  1174.         if ((error != earlyEOF) && (code < 0)) error = code;
  1175.  
  1176.     } /* while */
  1177.  
  1178.     if ((error != earlyEOF) && (code < 0)) error = code;
  1179.  
  1180.     return(error);
  1181.  
  1182. } /* parseFile */
  1183.  
  1184.  
  1185.  
  1186.  
  1187.  
  1188.  
  1189.  
  1190.  
  1191.  
  1192.  
  1193.  
  1194.